package la.iok.hzsvn.lewin.movie.operation.service;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import la.iok.hzsvn.lewin.kv.mybatis.entity.KeyValue;
import la.iok.hzsvn.lewin.kv.mybatis.service.KeyValueService;
import la.iok.hzsvn.lewin.movie.core.RoleEnum;
import la.iok.hzsvn.lewin.movie.core.Token;
import la.iok.hzsvn.lewin.movie.core.exception.ErrorCode;
import la.iok.hzsvn.lewin.movie.core.model.RoomVo;
import la.iok.hzsvn.lewin.movie.operation.entity.LuckyMoney;
import la.iok.hzsvn.lewin.movie.operation.mapper.LuckyMoneyMapper;
import la.iok.hzsvn.lewin.movie.operation.model.LuckyMoneyVo;
import la.iok.hzsvn.lewin.movie.services.MovieServiceInvoker;
import la.iok.hzsvn.lewin.mybatis.model.PageVo;
import la.iok.hzsvn.lewin.mybatis.service.BaseServiceImpl;
import la.iok.hzsvn.share.annotations.NotNull;
import la.iok.hzsvn.share.annotations.NotNullElement;
import la.iok.hzsvn.share.annotations.Nullable;
import la.iok.hzsvn.share.collection.IdOperators;
import la.iok.hzsvn.share.protocol.exception.ErrorCodeException;
import la.iok.hzsvn.share.tools.datetime.DateTimeUtil;
import la.iok.hzsvn.share.utils.CollectionUtils;
import la.iok.hzsvn.share.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class LuckyMoneyServiceImpl extends BaseServiceImpl<LuckyMoney, LuckyMoneyMapper,Long> implements LuckyMoneyService {
    private static final String[] SWITCH_STATUS = new String[]{"on", "off"};
    private static final Logger logger = LoggerFactory.getLogger(LuckyMoneyService.class);
    private final MovieServiceInvoker movieServiceInvoker;
    private final KeyValueService keyValueService;
    public LuckyMoneyServiceImpl(LuckyMoneyMapper mapper,
                                 MovieServiceInvoker movieServiceInvoker,
                                 KeyValueService keyValueService) {
        super(mapper);
        this.movieServiceInvoker = movieServiceInvoker;
        this.keyValueService = keyValueService;
    }

    @Override
    public Long saveReturnMoney(@Nullable Long roomId, @Nullable Integer amount) {
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(roomId,"房间id","不能为空");
        ErrorCode.PROPERTY_CHECK_ERROR.assertNotNull(amount,"金额","不能为空");
        ErrorCode.PROPERTY_CHECK_ERROR.assertTrue(amount>0,"金额","必须大于0");
        LuckyMoney r = currentReturnMoney(roomId);
        if(r == null){
            RoomVo roomVo = movieServiceInvoker.roomInfo(roomId);
            ErrorCode.ROOM_NOT_EXIST.assertNotNull(roomVo,roomId);
            LuckyMoney record = new LuckyMoney();
            record.setAmount(amount);
            record.setInitAmount(amount);
            record.setRoomId(roomId);
            record.setOrgUnitId(roomVo.getOrgUnitId());
            record.setType(LuckyMoney.TYPE_RETURN);
            Date now = new Date();
            Date end = DateTimeUtil.setTime(DateTimeUtil.monthEnd(now),23,59,59,0);
            record.setStart(now);
            record.setEnd(end);
            insertSelective(record);
            return record.getId();
        }else{
            logger.warn("房间[{}]已经存在[{}]到[{}]的返利记录",roomId,DateTimeUtil.format(r.getStart()),DateTimeUtil.format(r.getEnd()));
            return r.getId();
        }
    }

    @Override
    public LuckyMoney currentReturnMoney(@Nullable Long roomId) {
        if(roomId == null){
            return null;
        }
        return mapper.returnMoney(roomId,new Date());
    }

    @NotNull
    @Override
    @NotNullElement
    public List<LuckyMoney> currentReturnMoney(@Nullable Collection<Long> roomIds) {
        if(CollectionUtils.isEmpty(roomIds)){
            return Collections.emptyList();
        }
        roomIds = roomIds.stream().filter(Objects::nonNull).collect(Collectors.toSet());
        if(roomIds.isEmpty()){
            return Collections.emptyList();
        }
        return mapper.returnMoneyList(roomIds,new Date());
    }

    @Override
    public int use(LuckyMoney luckyMoney, int amount) {
        int times = 0;
        while (times<3){
            try{
                return doUse(luckyMoney,amount);
            }catch (ErrorCodeException e){
                ++times;
                if(times==3){
                    throw e;
                }else{
                    luckyMoney = select(luckyMoney.getId());
                }
            }
        }
        return 0;
    }

    @Override
    public PageVo<LuckyMoneyVo> page(@NotNull Token token, int page, int limit, @Nullable String orderColumn, @Nullable String order,
                                     @Nullable Long orgUnitId, @Nullable Long roomId, @Nullable Date start, @Nullable Date end) {
        orderColumn = StringUtils.isBlank(orderColumn)?"id":orderColumn;
        order = StringUtils.isBlank(order)?"ascending":order;
        order = "ascending".equalsIgnoreCase(order)?"ASC":"DESC";
        List<Long> orgUnits = null;
        if(token.hasAuthority(RoleEnum.Admin)){
            //nothing
        }else{
            orgUnits = movieServiceInvoker.childrenOrgUnitIds(token.getOrgUnitId());
            orgUnits = orgUnits == null?new ArrayList<>():new ArrayList<>(orgUnits);
            if(token.getOrgUnitId() != null){
                orgUnits.add(token.getOrgUnitId());
            }
            if(CollectionUtils.isEmpty(orgUnits)){
                return new PageVo<>(limit,page);
            }
        }

        Page<LuckyMoney> list = PageHelper.startPage(page,limit);
        mapper.list(orderColumn,order,orgUnits,orgUnitId,roomId,start,end);
        List<RoomVo> rooms = movieServiceInvoker.roomInfos(list.stream().map(LuckyMoney::getRoomId).collect(Collectors.toSet()));
        return page(list, item->po2vo(item,rooms));
    }

    @Override
    public void switchStatus(@NotNull Token token, @NotNull String status) {
        token.validAnyAuthority(RoleEnum.Admin);
        ErrorCode.PROPERTY_CHECK_ERROR.assertAnyOf(status,SWITCH_STATUS,()->new Object[]{"开关","只能取值on/off"});
        int ret = keyValueService.update("operation","returnMoney",status);
        if(ret == 0){
            //不存在
            KeyValue kv = new KeyValue();
            kv.setServerName("operation");
            kv.setKey("returnMoney");
            kv.setValue(status);
            keyValueService.insertSelective(kv);
        }
    }

    @Override
    public boolean switchStatus() {
        return "on".equals(returnMoneyStatus());
    }

    @Override
    public String switchStatus(Token token) {
        return returnMoneyStatus();
    }

    private String returnMoneyStatus(){
        return StringUtils.defaultIfBlank(keyValueService.selectKey("operation","returnMoney"),"on");
    }

    private <E> LuckyMoneyVo po2vo(@NotNull LuckyMoney item, List<RoomVo> rooms) {
        LuckyMoneyVo vo = new LuckyMoneyVo();
        BeanUtils.copyProperties(item,vo);
        RoomVo room = IdOperators.find(rooms,item.getRoomId());
        if(room != null){
            vo.setRoomName(room.getName());
            vo.setOrgUnitName(room.getOrgUnitName());
            vo.setOrgUnitId(room.getOrgUnitId());
        }
        return vo;
    }

    private int doUse(LuckyMoney luckyMoney, int amount){
        if(luckyMoney == null || luckyMoney.getAmount() == null || luckyMoney.getAmount() <= 0){
            //无法使用,直接返回0
            return 0;
        }
        if(amount <= 0){
            return 0;
        }
        int cost = Math.min(amount,luckyMoney.getAmount());
        int i = mapper.use(luckyMoney.getId(),luckyMoney.getAmount(),cost);
        // 减少余额
        ErrorCode.FINANCIAL_ACCOUNT_COST_ERROR.assertEqual(1,i);
        luckyMoney.setAmount(luckyMoney.getAmount() - cost);
        return cost;
    }
}
